package garbagen;

import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;

public class GCSimulation {
    // 模拟永久存活对象
    private static final List<byte[]> PERMANENT = new ArrayList<>();

    // 模拟长期存活对象（如缓存）
    private static final List<byte[]> CACHE = new ArrayList<>();
    // 模拟内存泄漏（静态引用导致无法回收）
    private static final List<byte[]> MEMORY_LEAK = new ArrayList<>();

    // 运行时间（分钟）
    public static int duration = 30;
    // 并发线程数
    public static int threadCountFast = 10;
    public static int threadCountSlow = 120;
    // 继续泄漏
    public static boolean simulatePermanent = false;
    public static boolean simulateLeaking = false;
    public static boolean simulateCache = false;
    public static boolean simulateHuge = false;
    public static boolean simulateFast = true;
    public static boolean simulateSlow = true;

    static final AtomicLong txns = new AtomicLong(0);
    static final AtomicLong txnsSlow = new AtomicLong(0);
    static long lastTxns = 0;
    static long lastTxnsSlow = 0;
    static AtomicLong totalTime = new AtomicLong(0);
    static AtomicLong totalTimeSlow = new AtomicLong(0);

    public static void main(String[] args) throws InterruptedException {
        System.out.printf("Starting simulation with %d threads for %d minutes\n",
                threadCountFast, duration);

        final AtomicBoolean running = new AtomicBoolean(true);

        // 添加永久存活的对象
        if(simulatePermanent) {
            for (int i = 0; i < 1024 * 200; i++) {
                PERMANENT.add(new byte[1024]); // 每次添加1k
            }
            System.out.println("Added 200MB to memory permanent area");
        }

        // 添加内存泄漏对象
        ScheduledExecutorService leakScheduler = Executors.newSingleThreadScheduledExecutor();
        leakScheduler.scheduleAtFixedRate(() -> {
            if(!simulateLeaking) {return;}
            synchronized (MEMORY_LEAK) {
                for (int i = 0; i < 10; i++) {
                    MEMORY_LEAK.add(new byte[1024 * 100]); // 每次添加100KB
                }
                System.out.println("Added 1MB to memory leak area");
            }
        }, 1, 1, TimeUnit.SECONDS);

        // 模拟缓存填充/清除周期
        ScheduledExecutorService cacheScheduler = Executors.newSingleThreadScheduledExecutor();
        cacheScheduler.scheduleAtFixedRate(() -> {
            if(!simulateCache) {return;}
            synchronized (CACHE) {
                if (CACHE.size() > 1000) {
                    CACHE.clear();
                    System.out.println("Cache cleared");
                } else {
                    CACHE.add(new byte[1024 * 50]); // 添加50KB缓存对象
                }
            }
        }, 0, 100, TimeUnit.MILLISECONDS);

        intervalPrint();

        // 工作线程模拟不同GC场景
        final ExecutorService fastExecutor = Executors.newFixedThreadPool(threadCountFast);
        for (int i = 0; i < threadCountFast; i++) {
            fastExecutor.execute(() -> {
                while (running.get()) {
                    if(simulateFast) {
                        simulateFastScenarios();
                    }else{
                        try {
                            TimeUnit.SECONDS.sleep(1);
                        } catch (InterruptedException e) {
                            Thread.currentThread().interrupt();
                        }
                    }
                }
            });

            // 延迟200毫秒钟再启动下一个线程
            try {
                TimeUnit.MILLISECONDS.sleep(200);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }

        final ExecutorService slowExecutor = Executors.newFixedThreadPool(threadCountSlow);
        for (int i = 0; i < threadCountSlow; i++) {
            slowExecutor.execute(() -> {
                while (running.get()) {
                    if(simulateSlow) {
                        simulateSlowScenarios();
                    }else {
                        try {
                            TimeUnit.SECONDS.sleep(1);
                        } catch (InterruptedException e) {
                            Thread.currentThread().interrupt();
                        }
                    }
                }
            });

            // 延迟1秒钟再启动下一个线程
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }

        // 运行指定时间后停止
        TimeUnit.MINUTES.sleep(duration);
        running.set(false);
        fastExecutor.shutdownNow();
        leakScheduler.shutdownNow();
        cacheScheduler.shutdownNow();
    }

    private static void intervalPrint() {
        int period = 5;
        ScheduledExecutorService printScheduler = Executors.newSingleThreadScheduledExecutor();
        printScheduler.scheduleAtFixedRate(() -> {
            long cnt = txns.get()-lastTxns;
            long cntSlow = txnsSlow.get()-lastTxnsSlow;
            System.out.printf("tps = %d, txns = %s, avg= %d; tps2 = %d, txns2 = %s, avg2= %d \n",
                    cnt/period, cnt, cnt>0?totalTime.get() / cnt:0,
                    cntSlow/period, cntSlow, cntSlow>0?totalTimeSlow.get() / cntSlow:0
            );
//            G1GCUtilPrinter.printG1GcUtil();
            lastTxns = txns.get();
            lastTxnsSlow = txnsSlow.get();
            totalTime = new AtomicLong(0);
            totalTimeSlow = new AtomicLong(0);

        }, 0, period, TimeUnit.SECONDS);
    }

    private static void simulateFastScenarios() {
        double rate = ThreadLocalRandom.current().nextDouble();
        if (rate < 0.1) {
            simulateFastTxn(ThreadLocalRandom.current().nextInt(50, 100));
        }else if (rate < 0.2) {
            simulateFastTxn(ThreadLocalRandom.current().nextInt(100, 300));
        }else if (rate < 0.3) {
            simulateFastTxn(ThreadLocalRandom.current().nextInt(100, 300));
        }else if (rate < 0.4) {
            simulateFastTxn(ThreadLocalRandom.current().nextInt(300, 400));
        }else if (rate < 0.5) {
            simulateFastTxn(ThreadLocalRandom.current().nextInt(400, 500));
        }else if (rate < 0.6) {
            simulateFastTxn(ThreadLocalRandom.current().nextInt(500, 600));
        }else if (rate < 0.7) {
            simulateFastTxn(ThreadLocalRandom.current().nextInt(600, 700));
        }else if (rate < 0.8) {
            simulateFastTxn(ThreadLocalRandom.current().nextInt(700, 800));
        }else if (rate < 0.9) {
            simulateFastTxn(ThreadLocalRandom.current().nextInt(800, 900));
        }else{
            simulateFastTxn(ThreadLocalRandom.current().nextInt(900, 1500));
        }
    }

    private static void simulateSlowScenarios() {
        simulateSlowTxn(ThreadLocalRandom.current().nextInt(9000, 10000));
    }

    private static void simulateFastTxn(int lifeMs) {
        txns.incrementAndGet();
        long start = System.currentTimeMillis();

        // 创建短命对象（1ms生命周期）
        Object oo = createTransientObjects(ThreadLocalRandom.current().nextInt(10000, 20000));
        int i = oo.hashCode();

        Object obj = createMediumLivedObjects(1000, lifeMs);
        int i1 = obj.hashCode();

        totalTime.addAndGet(System.currentTimeMillis() - start);
    }

    private static void simulateSlowTxn(int lifeMs) {
        txnsSlow.incrementAndGet();
        long start = System.currentTimeMillis();
        Object obj = createMediumLivedObjects(4000, lifeMs);
        totalTimeSlow.addAndGet(System.currentTimeMillis() - start);
    }


    private static Object createTransientObjects(int count) {
        long sum = 0;
        for (int i = 0; i < count; i++) {
            // 99.9%的短命对象（存活时间 < 1ms）
            byte[] shortLived = new byte[ThreadLocalRandom.current().nextInt(256)];
            sum += shortLived.length;
            // 不做引用，快速变成垃圾
        }
        return sum;
    }

    private static Object createMediumLivedObjects(int count, int life) {
        byte[][] mediumLived = new byte[count][];
        for (int i = 0; i < count; i++) {
            mediumLived[i] = new byte[ThreadLocalRandom.current().nextInt(256, 2048)];
        }

        // 随机创建大型对象（10MB以上
        Object huge = null;
        if (simulateHuge && ThreadLocalRandom.current().nextDouble() < 0.0005) {
            huge = createHugeObjects();
        }
        try {
            TimeUnit.MILLISECONDS.sleep(ThreadLocalRandom.current().nextInt(Math.max(life - 10, 0), life+10));
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        Object ssss;
        if(huge!=null){
            ssss = huge.hashCode() + huge.hashCode();
        }
        return mediumLived;
    }

    private static Object createHugeObjects() {
        // 随机创建1-50MB大对象
        int sizeMB = ThreadLocalRandom.current().nextInt(1, 15);
        byte[] huge = new byte[sizeMB * 1024 * 1024];
        System.out.println("Created " + sizeMB + "MB object");
        return huge;
    }
}